package org.vaadin.vaadinjit;

import java.lang.reflect.Method;
import java.util.Map;

import org.vaadin.vaadinjit.data.JitTreeData;
import org.vaadin.vaadinjit.widgetset.client.ui.VJitSpaceTree;

import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;

/**
 * SpaceTree visualization.
 * 
 * @author jonkka
 *
 */
@com.vaadin.ui.ClientWidget(VJitSpaceTree.class)
public class JitSpaceTree extends JitComponent<JitTreeData> {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	private static final Method NODE_CLICKED_METHOD;
	
	static {
		try {
			NODE_CLICKED_METHOD = NodeClickListener.class.getDeclaredMethod(
					"nodeClicked", new Class[] {NodeClickEvent.class});

			
		} catch (NoSuchMethodException e) {
			throw new RuntimeException(
					"Internal error finding methods in JitChartComponent");
		}
	}
	
	private Orientation orientation = Orientation.LEFT;
	private boolean orientationChanged;
	
	private Alignment alignment = Alignment.CENTER;
	private boolean alignmentChanged;
	
	
	@Override
	public void setJitData(JitTreeData jitData) {
		super.setJitData(jitData);
		setDataChanged();
	}
	
	@Override
	public void paintContent(PaintTarget target) throws PaintException {
		super.paintContent(target);
		if (orientationChanged) {
			String treeOrient;
			switch (orientation) {
				case LEFT: treeOrient = "left"; break;
				case RIGHT: treeOrient = "right"; break;
				case TOP: treeOrient = "top"; break;
				case BOTTOM: treeOrient = "bottom"; break;
				default: treeOrient = "left"; break;
			}
			target.addAttribute("treeOrient", treeOrient);
			orientationChanged = false;
		}
		if (alignmentChanged) {
			String treeAlign;
			switch (alignment) {
				case LEFT: treeAlign = "left"; break;
				case RIGHT: treeAlign = "right"; break;
				case CENTER: treeAlign = "center"; break;
				default: treeAlign = "left"; break;
			}
			target.addAttribute("treeAlign", treeAlign);
			orientationChanged = false;
		}
	}
	
	
	/**
	 * Receive and handle events and other variable changes from the client.
	 * 
	 * {@inheritDoc}
	 */
	@Override
	public void changeVariables(Object source, Map<String, Object> variables) {
		super.changeVariables(source, variables);
		
		if (variables.containsKey(VJitSpaceTree.NODE_CLICK_IDENTIFIER)) {
			String nodeId = variables.get(VJitSpaceTree.NODE_CLICK_IDENTIFIER).toString();
			// Fire a hover label event
			fireEvent(new NodeClickEvent(this, nodeId));
		}
	}
	
	
	/**
	 * Set number of levels in teh tree to show on inialization
	 * 
	 * @param levels number of levels
	 */
	public void setLevelsToShow(int levels) {
		setOption("levelsToShow", levels);
	}
	
	
	/**
	 * Set the distance between nodes.
	 * 
	 * @param levelDistance the distance
	 */
	public void setLevelDistance(int levelDistance) {
		setOption("levelDistance", levelDistance);
	}
	
	/**
	 * Set the animation time in milliseconds
	 * 
	 * @param time the time in milliseconds
	 */
	public void setAnimationDuration(int time) {
		setOption("duration", time);
	}
	
	/**
	 * Set if all levels in the tree should be shown when the
	 * visualization is initialized
	 * 
	 * @param value true if the tree should be constrained, else false
	 */
	public void setConstrained(boolean value) {
		setOption("constrained", value);
	}

	/**
	 * Set the orientation of the SpaceTree
	 * 
	 * @param o the orientation
	 */
	public void setOrientation(Orientation o) {
		orientation = o;
		orientationChanged = true;
		this.requestRepaint();
	}
	
	/**
	 * Get the orientation of the tree
	 * 
	 * @return the current orientation
	 */
	public Orientation getOrientation() {
		return orientation;
	}
	
	/**
	 * Get the alignment of the tree
	 * 
	 * @return the alignment
	 */
	public Alignment getAlignment() {
		return alignment;
	}
	
	/**
	 * Set the alignment of the tree
	 * 
	 * @param a the alignment
	 */
	public void setAlignment(Alignment a) {
		this.alignment = a;
		alignmentChanged = true;
		this.requestRepaint();
	}
	
	/**
	 * Add a listener for node clicking events
	 * 
	 * @param listener the listener
	 */
	public void addListener(NodeClickListener listener) {
		addListener(NodeClickEvent.class, listener, NODE_CLICKED_METHOD);
	}
	
	/**
	 * Interface to be implemented by objects which wich to listen on 
	 * node click events.
	 * 
	 * @author jonkka
	 *
	 */
	public interface NodeClickListener {
		
		public void nodeClicked(NodeClickEvent e);
		
	}
	
	/**
	 * Events created and sent to listeners on node clicks.
	 * 
	 * @author jonkka
	 *
	 */
	public class NodeClickEvent extends Event {
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		
		private String nodeId;
		
		public NodeClickEvent(JitSpaceTree source, String nodeId) {
			super(source);
			this.nodeId = nodeId;
		}
		
		public String getNodeId() {
			return nodeId;
		}
	}
	
	/**
	 * Orientation of the tree
	 * 
	 * @author jonkka
	 *
	 */
	public enum Orientation {
		RIGHT,
		LEFT,
		TOP,
		BOTTOM;		
	}
	
	/**
	 * Alignment of the tree
	 * 
	 * @author jonkka
	 *
	 */
	public enum Alignment {
		LEFT,
		RIGHT,
		CENTER;
	}
}
